#include "TwoWayList.h"
#include "Record.h"
#include "Schema.h"
#include "File.h"
#include "Comparison.h"
#include "ComparisonEngine.h"
#include "SortedDBFile.h"
#include "Defs.h"
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <fstream>

using namespace std;


Pipe inputPipe(100);
Pipe outputPipe(100);

//Constructor for SortedDBFile class
SortedDBFile::SortedDBFile(){

}

//Constructor to pass structure containing ordermaker and runlength
SortedDBFile::SortedDBFile(struct SortInfo* mySortInformation){

	sortOrder = mySortInformation->myOrder;
	runLength= mySortInformation->runLength;
	currentMode = start;
	myFile = new File;
	tempFile = new File;
	pagePointer = 0;

}

//Destructor for SortedFile class
SortedDBFile::~SortedDBFile(){
	delete myFile;
}


void SortedDBFile::DumpRecordToFile(Record* addMe,Page &currentPage,File *addTo){

	if(currentPage.Append(addMe)==0){
		cout<<"IN DumpRecordToFile :: Record not being appended"<<endl;
		if(addTo->GetLength()==0){
			addTo->AddPage(&currentPage,addTo->GetLength());
		}else{
			addTo->AddPage(&currentPage,addTo->GetLength()-1);
		}
		currentPage.EmptyItOut();
		currentPage.Append(addMe);
	}
	//cout

}

//Gets record from file via page returns 1 on success returns 0 when nothing left to be read
int SortedDBFile::GetRecordFromFile(Record *getMe, Page &currentPage, File* fromMe, int &pageNumber){

	if(currentPage.GetFirst(getMe)==0){
		if(pageNumber<=fromMe->GetLength()-2){//*********Can be problem here
			fromMe->GetPage(&currentPage,pageNumber++);
			currentPage.GetFirst(getMe);
			return 1;
		}else{   //All the records from file has been read
			return 0;
		}
	}
	return 1;
}

//Function to merge records from pipe and already sorted file on disk into new tempFile and then make it current sorted file
void SortedDBFile::merge(){

	tempFile->Open(0,"tempFile");
	pagePointer = 0;
	Record* fromPipe;
	Record* fromOrigFile;
	ComparisonEngine compEng;

	if(myFile->GetLength()!=0){

		myFile->GetPage(&bufferPage,pagePointer);
		pagePointer++;
		bufferPage.GetFirst(fromOrigFile);
		outputPipe.Remove(fromPipe);
		while((fromPipe!=NULL)&&(fromOrigFile!=NULL)){

			if(compEng.Compare(fromPipe,fromOrigFile,sortOrder)>=0){ //Means pipe is bigger therefore extract next record from original file only
				//if(tempPage.Append(fromOrigFile)==0){

				//Now dump this page to the tempFile
				//empty the page
				//add this record to the page
				DumpRecordToFile(fromOrigFile,tempPage,tempFile);

				//tempPage.Append(fromOrigFile);
				//}

				//Now check buffer page if it is having records then fetch that record
				//Else try to get next page form the file and increment page ccounter
				//if you don't get the page then dump all reamining records from pipe to tempFile
				//if you get the page then make it the buffer page and get its first record into the fromOrigFile
				if(GetRecordFromFile(fromOrigFile,bufferPage,myFile,pagePointer)==0){
					//Dump all records from pipe to file
					while(outputPipe.Remove(fromPipe)){
						DumpRecordToFile(fromPipe,tempPage,tempFile);
					}
				}


			}else{//Means origFile is bigger then get next record from pipe
				//if(tempPage.Append(fromPipe)==0){

				DumpRecordToFile(fromPipe, tempPage,tempFile);
				//Now dump this page to the tempFile
				//empty the page
				//add this record to the page
				//tempPage.Append(fromPipe);
				if((outputPipe.Remove(fromPipe))==0){
					//add remaining file records to tempFile
					while(GetRecordFromFile(fromOrigFile,bufferPage,myFile,pagePointer)){
						DumpRecordToFile(fromOrigFile,tempPage,tempFile);
					}
				}

				//}

			}
		}

	}else{
		//If file is empty but we have records in pipe that is user does load and then close or add and then close
		//Add records from pipe into the tempFile
		//**Debugging information
		fromPipe =new Record;		//**

		while(outputPipe.Remove(fromPipe)){
			//fromPipe->Print(mySchema);//**
			DumpRecordToFile(fromPipe,tempPage,tempFile);

			fromPipe =new Record;

		}
	}
	/*Record temp;
	Schema* mySchema = new Schema("catalog","region");//**
	while(tempPage.GetFirst(&temp)){
		temp.Print(mySchema);
	}
	cout<<"Inside merge:: Pagepointer "<<pagePointer<<endl;*/
	//pagePointer=2;
	tempFile->AddPage(&tempPage,pagePointer/*tempFile->GetLength()*/);

	tempFile->Close();//**

}

//This function adds the records to the sortedDBfile
void SortedDBFile::AddToFile(Record &addme){


}

//This function changes current mode of sorted db file to writing from reading or start mode
void SortedDBFile::ChangeToWrite(){

	//**There can be error in passing arguments
	//Call BigQ constructor
	//need to delete it somewhere
	myBigQ = new BigQ(inputPipe,outputPipe,*sortOrder, runLength);

	//change mode
	currentMode = writing;
}

//This function changes current mode of sorted dbfile to read from writing or start mode
void SortedDBFile::ChangeToRead(){

	/*Record* recordFromOutputPipe;
	recordFromOutputPipe = new Record;*/
	Record recordFromOutputPipe;
	File sortedFile;
	inputPipe.ShutDown();

	//Schema *mySchema = new Schema("catalog","customer");

	//**This is where there can be error in case of wrong results or segmentation faults
	/*while(outputPipe.Remove(&recordFromOutputPipe)){
		//Add this record to the end of the sorted file
		recordFromOutputPipe.Print(mySchema);
	}*/
	//delete mySchema;

	//Now merge the records from myFile and pipe into tempFile and then exchange the files
	merge();

	//Exchange myfile and tempfile
	outputPipe.ShutDown();
	currentMode = reading;
	delete myBigQ;
}

void SortedDBFile::Add(Record &addme){

	if(currentMode == writing){
		//Insert into input pipe
		inputPipe.Insert(&addme);
	}else{
		//Means currentMode is either start or reading
		//If add is called in this case mode should be changed to writing
		inputPipe.Insert(&addme);
		ChangeToWrite();
	}

}

void SortedDBFile::Load(Schema &myschema, char* loadpath){

}

void SortedDBFile::MoveFirst(){

}

int SortedDBFile::Create(char* f_path){
	myFile->Open(0,f_path);
	return 1;
}

int SortedDBFile::Open(char* fpath){
	myFile->Open(1,fpath);
	cout<<"Inside SortedDBFile :: Open "<<endl;
	sortOrder->Print();
	return 1;

}

int SortedDBFile::GetNext(Record &fetchme){

}

int SortedDBFile::GetNext(Record &fetchme,CNF &cnf, Record &literal){

}

int SortedDBFile::Close(){
	ChangeToRead();
	return 1;
}
